跳到主要内容

多臂老虎机

多臂老虎机是什么?

多臂老虎机(Multi-Armed Bandit, MAB)模型是一个理想的工具,用于理解和优化在不确定性决策中的探索(exploration)和利用(exploitation)之间的平衡。这个模型借鉴自赌博机(老虎机),其中每个“臂”代表一个决策选项,每个选项都有其自己的回报概率分布。

  • 探索(Exploration):试验不同的选项(臂),以获取有关每个选项回报的信息。
  • 利用(Exploitation):根据已有信息,选择看似最佳的选项,以最大化即时回报。
  • 遗憾(Regret):指的是与始终选择最佳选项相比所失去的累积回报。这是评估多臂老虎机策略性能的关键指标。

在多臂老虎机问题中,几个核心的数学概念包括:

  1. 回报的期望值:假设每个臂 ii 的回报遵循某个概率分布,期望回报表示为 μi\mu_i。因此,对于每个臂 ii,其期望值可以表示为 μi=E[Ri]\mu_i = \mathbb{E}[R_i],其中 RiR_i 是从臂 ii 获得的回报。

  2. 累积遗憾:假设有 KK 个臂,每个臂的期望回报为 μi\mu_i,最佳臂的期望回报为 μ\mu^*(它是一个未知的经验值)。如果在 TT 次尝试中,第 tt 次选择了臂 XtX_t,那么累积遗憾 RTR_T 可以表示为 RT=Tμt=1TμXtR_T = T \cdot \mu^* - \sum_{t=1}^{T} \mu_{X_t}

提示

在数学和统计学中,符号 E[Ri]\mathbb{E}[R_i] 表示随机变量 RiR_i 的期望值(expected value),也就是该随机变量的平均值或平均回报。期望值是概率论中的一个核心概念,用于描述随机变量在多次试验或观测中平均的结果。

具体来说,在多臂老虎机问题中,每个臂 ii 提供的回报 RiR_i 可以被视为一个随机变量,因为每次拉动臂的结果可能不同。这个结果通常受到某种概率分布的影响,例如正态分布、伯努利分布等。那么,E[Ri]\mathbb{E}[R_i] 就是表示在大量重复拉动该臂的情况下,平均每次可以得到的回报。

数学上,对于离散随机变量,其期望值计算公式为:

E[Ri]=rrP(Ri=r)\mathbb{E}[R_i] = \sum_{r} r \cdot P(R_i = r)

其中,rr 表示可能的回报值,P(Ri=r)P(R_i = r) 是获得该回报的概率。

对于连续随机变量,则通过积分计算期望值:

E[Ri]=rf(r)dr\mathbb{E}[R_i] = \int r \cdot f(r) \, dr

这里,f(r)f(r)RiR_i 的概率密度函数。

在多臂老虎机问题中,计算每个臂的期望值有助于评估其长期表现,从而指导决策者在探索和利用之间做出更有效的选择。

累积遗憾

这里详细的介绍下第二个概念:累积遗憾。

累积遗憾(cumulative regret)在多臂老虎机问题中是一个关键概念,用于衡量在一系列决策中与理论上的最佳策略相比所损失的总回报。让我们详细解释一下这个概念,包括为什么不能仅仅基于初始概率来始终选择同一个臂。

设想一个多臂老虎机有 KK 个臂,每个臂 ii 的期望回报率是 μi\mu_i。在理想情况下,如果我们知道哪个臂有最高的期望回报率,我们会一直选择那个臂。设最佳臂的期望回报率为 μ\mu^*。在一系列的 TT 次尝试中,如果我们每次都选择这个最佳臂,理论上可以获得的总回报是 TμT \cdot \mu^*

然而,在实际情况中,我们初始并不知道哪个臂的期望回报率最高。因此,我们需要通过尝试不同的臂来估计每个臂的回报率。在 TT 次尝试中,假设第 tt 次尝试选择的臂是 XtX_t,那么实际获得的总期望回报是 t=1TμXt\sum_{t=1}^{T} \mu_{X_t}

累积遗憾,即与理论上的最佳策略相比的总回报损失,可以表示为:

RT=Tμt=1TμXtR_T = T \cdot \mu^* - \sum_{t=1}^{T} \mu_{X_t}

在多臂老虎机问题中,μ\mu^* 表示所有臂中具有最高期望回报的臂的期望回报率。然而,在实践中,确定 μ\mu^* 是一个挑战,因为我们通常不会事先知道每个臂的真实期望回报率。以下是如何估计 μ\mu^* 的一些方法:

  1. 经验估计:在多次尝试和探索之后,我们可以根据每个臂的历史表现来估计其期望回报率。对于每个臂 ii,我们计算到目前为止从该臂获得的所有回报的平均值,作为 μi\mu_i 的估计。然后,选择这些估计值中最高的作为 μ\mu^* 的估计。

  2. 增量更新:为了实时更新每个臂的期望回报估计,我们可以采用增量更新方法。每次从臂 ii 获得新的回报后,我们更新该臂的期望回报估计。这种方法适用于需要适应动态变化的环境。

  3. 探索-利用策略:使用如ε-贪婪(ε-greedy)、UCB(Upper Confidence Bound)或汤普森采样(Thompson Sampling)等策略,可以在探索和利用之间进行权衡。这些策略在一定程度上保证了所有臂都有被尝试的机会,从而提高了找到真正的 μ\mu^* 的可能性。

  4. 统计方法:可以使用统计测试(如置信区间)来确定哪个臂的表现统计上显著优于其他臂。这可以帮助识别最可能具有最高期望回报的臂。

在理想情况下,随着尝试次数的增加,我们对每个臂的期望回报率的估计将逐渐接近其真实值,从而 μ\mu^* 的估计也将更加准确。但在实际应用中,由于时间和资源的限制,我们通常只能获得对 μ\mu^* 的近似估计。这种不确定性是多臂老虎机问题中的一个核心挑战,也是为什么需要在探索和利用之间找到合适平衡的原因。

现实生活的应用

让我们通过一个具体的应用示例来更好地理解这些概念:

假设一家公司有多种不同风格的广告,并希望确定哪一种最有效地吸引用户点击。在这个例子中,每种广告相当于一个“臂”。最初,公司不知道哪种广告的效果最好,因此需要在不同的广告(探索)之间进行切换,以收集哪种广告获得更高点击率的数据。随着时间的推进,某些广告可能显现出更高的平均点击率,此时公司倾向于更频繁地展示这些广告(利用)。数学上,公司的目标是最大化点击率的期望值,同时最小化与始终选择最佳广告相比的累积遗憾。

通过这个模型,公司能够在收集足够信息(探索)和利用已知最佳选项(利用)之间找到平衡,从而优化广告策略。多臂老虎机模型提供了一个强大的框架,用于在动态和不确定的环境中作出基于数据的决策,这在现代业务和科技应用中非常重要。

使用代码说明

import numpy as np
import matplotlib.pyplot as plt

class MultiArmedBandit:
"""
多臂老虎机模拟。
"""
def __init__(self, n_arms, epsilon):
"""
初始化多臂老虎机。

参数:
n_arms (int): 老虎机的臂数量。
epsilon (float): 用于ε-贪婪策略的探索概率。
"""
self.n_arms = n_arms
self.epsilon = epsilon
self.arm_values = np.random.rand(n_arms) # 每个臂的真实回报概率
self.k = np.zeros(n_arms) # 每个臂被选择的次数
self.q = np.zeros(n_arms) # 每个臂的估计值

def choose_arm(self):
"""
使用ε-贪婪策略选择臂。
"""
if np.random.rand() < self.epsilon:
return np.random.choice(self.n_arms) # 探索
else:
return np.argmax(self.q) # 利用

def update(self, chosen_arm, reward):
"""
更新对选择臂的估计值。

参数:
chosen_arm (int): 被选择的臂。
reward (float): 获得的回报。
"""
self.k[chosen_arm] += 1
self.q[chosen_arm] += (reward - self.q[chosen_arm]) / self.k[chosen_arm]

def run(self, n_steps):
"""
运行多臂老虎机模拟。

参数:
n_steps (int): 尝试的次数。
"""
rewards = np.zeros(n_steps)
for step in range(n_steps):
chosen_arm = self.choose_arm()
reward = np.random.binomial(1, self.arm_values[chosen_arm])
self.update(chosen_arm, reward)
rewards[step] = reward
return rewards

# 设置参数并运行多臂老虎机
n_arms = 5 # 老虎机臂数量
epsilon = 0.1 # ε-贪婪策略中的探索概率
n_steps = 1000 # 尝试的次数

bandit = MultiArmedBandit(n_arms, epsilon)
rewards = bandit.run(n_steps)

# 绘制累积奖励图
plt.plot(np.cumsum(rewards))
plt.title('Multi-Armed Bandit Performance')
plt.xlabel('Steps')
plt.ylabel('Cumulative Reward')
plt.show()

输出

解释一下这里的 reward = np.random.binomial(1, self.arm_values[chosen_arm]),这个函数是用来生成模拟回报的表达式,它使用了二项分布来模拟从选择的臂获得的回报。

  • np.random.binomial(n, p):这是一个 NumPy 函数,用于从二项分布中生成随机样本。二项分布是一种常见的概率分布,用于描述在固定次数的独立试验中成功的次数,其中每次试验成功的概率都是相同的。
  • n:在 np.random.binomial 函数中,n 表示试验的次数。在这个场景中,n 被设为 1,意味着每次只进行一次试验。
  • p:这是每次试验成功的概率。在这个例子中,self.arm_values[chosen_arm] 表示被选中的臂的成功概率。每个臂都有一个固定的概率,表示从该臂获得回报的概率。

因此,np.random.binomial(1, self.arm_values[chosen_arm]) 表示对于被选中的臂,进行一次试验(拉杆),根据该臂的成功概率来确定是否获得回报(1 表示获得回报,0 表示没有获得回报)。

这种方法在模拟多臂老虎机问题时非常有用,因为它可以模拟每次尝试的随机性和不确定性。在实际应用中,每次拉动臂可能会有不同的结果,而二项分布提供了一种简单的方式来模拟这种情况。

在这个多臂老虎机的例子中,虽然每次选择臂时都有一定概率进行随机选择(探索),但最后取得的奖励数量逐步上升的原因在于ε-贪婪策略中的“利用”部分以及回报更新机制。让我们详细解释这个过程:

ε-贪婪策略在每次选择臂时进行以下判断:

  1. 探索(Exploration):有 ϵ\epsilon 的概率随机选择一个臂。这部分确保了每个臂都有被尝试的机会,即使它目前看起来不是最佳选择。
  2. 利用(Exploitation):有 1ϵ1 - \epsilon 的概率选择当前估计回报最高的臂。这是通过 np.argmax(self.q) 实现的,即选择当前估计值(self.q)最高的臂。

每次选择臂并获得回报后,这个臂的估计值(self.q)会根据获得的回报进行更新。这意味着随着时间的推移,每个臂的估计值将逐渐接近其真实回报率。因此,利用步骤中选择的臂更有可能是真正的最优臂。

在初始阶段,由于对每个臂的估计还不准确,选择可能不是最优的,但随着时间的推移和更多的尝试,以下几点逐渐发生:

  • 更准确的估计:每个臂的估计值变得更接近其真实回报率。
  • 更频繁的最佳选择:利用步骤越来越可能选择真正的最优臂,因为估计值更准确。
  • 累积奖励增加:随着最优臂被更频繁地选择,累积的奖励逐渐增加。

即使每次选择臂时进行随机选择的概率(由 ϵ\epsilon 控制)是相同的,但随着对每个臂的了解逐渐增加,模型能够更有效地进行利用,从而使得累积奖励随时间上升。这正是ε-贪婪策略中探索和利用之间平衡的结果。

常见的几种策略

上面的代码中使用了ε-贪婪策略,它是一种常见的多臂老虎机策略。除了ε-贪婪策略之外,还有其他一些策略可以在探索和利用之间进行权衡,例如:

多臂老虎机问题有几种主流的策略,用于平衡探索和利用。下面介绍几种常见的策略及相应的简单代码示例。

1. ε-贪婪策略(ε-Greedy)

这种策略在大部分时间选择当前估计最好的臂(利用),但以一定的小概率(ε)随机选择任一臂(探索)。

代码示例:

import numpy as np

def epsilon_greedy(q_values, epsilon):
if np.random.random() < epsilon:
return np.random.randint(len(q_values)) # 探索
else:
return np.argmax(q_values) # 利用

# 假设有4个臂的估计值
q_values = [0.5, 0.6, 0.7, 0.4]
epsilon = 0.1

# 选择臂
arm = epsilon_greedy(q_values, epsilon)

2. Upper Confidence Bound(UCB)

UCB策略考虑了每个臂的估计值和不确定性(基于选择该臂的次数)。它倾向于选择具有最高上置信界的臂。

代码示例:

import numpy as np

def ucb(q_values, counts, total_counts):
ucb_values = q_values + np.sqrt(2 * np.log(total_counts) / (counts + 1e-5))
return np.argmax(ucb_values)

# 假设有4个臂的估计值和选择次数
q_values = [0.5, 0.6, 0.7, 0.4]
counts = [10, 12, 15, 8]
total_counts = sum(counts)

# 选择臂
arm = ucb(q_values, counts, total_counts)

在 Upper Confidence Bound(UCB)策略中,ucb_values = q_values + np.sqrt(2 * np.log(total_counts) / (counts + 1e-5)) 这行代码用于计算每个臂的UCB值,这些值用于决定选择哪个臂。这个表达式的每个部分代表以下含义:

  1. q_values:这是一个数组,其中的每个元素代表一个臂的估计回报值。这个估计是基于过去从该臂获得的回报进行计算的。
  2. total_counts:这是到目前为止所有臂被选择的总次数。
  3. counts:这是一个数组,每个元素记录了对应臂被选择的次数。

UCB计算:

  • np.sqrt(2 * np.log(total_counts) / (counts + 1e-5)):这部分计算的是每个臂的置信区间宽度。这里使用对数函数 np.log(total_counts) 来确保随着总尝试次数的增加,对于尚未充分探索的臂给予更多的优先级。counts + 1e-5 避免了除以零的情况(1e-5 是一个非常小的数,用来确保分母不为零)。

  • 整个表达式 q_values + np.sqrt(2 * np.log(total_counts) / (counts + 1e-5)):这里将每个臂的估计回报(q_values)和其置信区间的宽度相加。这样做的目的是平衡每个臂的已知性能(估计回报)和不确定性(置信区间宽度)。结果就是UCB值,它既考虑了臂的平均表现,也考虑了对该臂的了解程度。

所以可以看出臂的UCB值由两部分组成:估计回报(q_values)和置信区间的宽度(由 np.sqrt(2 * np.log(total_counts) / (counts + 1e-5)) 计算)。随着实验次数的增加,算法越来越倾向于利用(选择表现最好的臂),但对数项确保即使在实验进行很长时间之后,仍然会有一定程度的探索。这有助于避免过早地集中于某个臂,而忽略了可能更优的其他臂。

提示

随着 total_counts(所有臂的总选择次数)的增加,np.log(total_counts) 逐渐增长,但增长速度会逐渐放缓(对数函数的性质)。这意味着随着时间的推移,算法会增加对每个臂的探索,但这种增加是逐渐减缓的。这有助于确保即使在实验后期,对不熟悉的臂仍有一定程度的探索。

在UCB策略中,选择具有最高UCB值的臂。较高的UCB值可能是由于较高的估计回报,或者由于较大的不确定性(即较少的尝试次数),后者鼓励对不熟悉的臂进行探索。

上面的说明还是太抽象了,下面使用一个简单的数学例子来说明 Upper Confidence Bound(UCB)策略是如何工作的。

UCB计算过程

假设我们有一个多臂老虎机问题,其中有3个臂。以下是这些臂被选择的次数和它们的估计回报(基于之前的选择):

  • 臂 1: 被选择 10 次,估计回报为 0.5
  • 臂 2: 被选择 20 次,估计回报为 0.6
  • 臂 3: 被选择 5 次,估计回报为 0.4

现在,我们假设总共已经进行了 35 次选择(每个臂的选择次数之和)。

对于每个臂,我们需要计算它的UCB值。UCB值由两部分组成:估计回报和置信区间宽度。置信区间宽度计算如下:

UCB=估计回报+2×ln(总选择次数)臂的选择次数\text{UCB} = \text{估计回报} + \sqrt{\frac{2 \times \ln(\text{总选择次数})}{\text{臂的选择次数}}}

其中,ln\ln 表示自然对数。

  1. 臂 1 的 UCB

    0.5+2×ln(35)100.5+0.83=1.330.5 + \sqrt{\frac{2 \times \ln(35)}{10}} \approx 0.5 + 0.83 = 1.33
  2. 臂 2 的 UCB

    0.6+2×ln(35)200.6+0.59=1.190.6 + \sqrt{\frac{2 \times \ln(35)}{20}} \approx 0.6 + 0.59 = 1.19
  3. 臂 3 的 UCB

    0.4+2×ln(35)50.4+1.18=1.580.4 + \sqrt{\frac{2 \times \ln(35)}{5}} \approx 0.4 + 1.18 = 1.58

根据UCB值,我们将选择UCB值最高的臂,即臂 3(UCB = 1.58)。尽管臂 3 的估计回报较低(只有0.4),但由于它被选择的次数相对较少,其不确定性较高,因此其UCB值更高。这就是UCB策略的核心:它鼓励对那些可能具有潜力但尚未被充分探索的臂进行探索。

通过这个例子,我们可以看到,UCB策略在决定选择哪个臂时不仅考虑了估计回报,还考虑了每个臂的不确定性(即被选择的频率)。这有助于在探索未知和利用已知之间找到平衡,特别是在长期的实验过程中。

总的来说,这个UCB表达式帮助在探索(尝试那些不确定性较高的臂)和利用(选择那些似乎回报较高的臂)之间找到平衡,这对于高效解决多臂老虎机问题至关重要。

3. 汤普森采样(Thompson Sampling)

汤普森采样是一种基于贝叶斯方法的策略,它为每个臂的回报率分布生成一个样本,然后选择具有最高样本值的臂。

代码示例:

import numpy as np

def thompson_sampling(successes, failures):
sample_means = np.random.beta(successes + 1, failures + 1)
return np.argmax(sample_means)

# 假设有4个臂的成功和失败次数
successes = [5, 7, 6, 3]
failures = [5, 3, 4, 7]

# 选择臂
arm = thompson_sampling(successes, failures)

在汤普森采样(Thompson Sampling)策略中,sample_means = np.random.beta(successes + 1, failures + 1) 这行代码的作用是从贝塔分布中为每个臂生成一个样本值。这些样本值用于决定哪个臂将被选择。让我们分解这个表达式:

  1. 贝塔分布(Beta Distribution):贝塔分布是一种在 [0, 1] 区间内的连续概率分布,通常用于模拟随机变量的概率。它由两个参数 α(alpha)和 β(beta)控制,分别对应于成功和失败的次数。

  2. np.random.beta(a, b):这是 NumPy 中用于从贝塔分布生成随机样本的函数,其中 ab 是分布的两个参数。

  3. successes + 1failures + 1:这里 successesfailures 分别表示从某个臂获得奖励(成功)和未获得奖励(失败)的次数。在贝塔分布中,successes + 1 作为参数 α(alpha),failures + 1 作为参数 β(beta)。加 1 是一种常见的技巧,称为“拉普拉斯平滑”(Laplace Smoothing),用于避免任何参数为零的情况,它可以在没有观测到数据(即成功或失败次数为零)时提供一个基线的概率。

因此,sample_means = np.random.beta(successes + 1, failures + 1) 生成的是每个臂基于其历史成功和失败记录的概率估计的样本值。在汤普森采样策略中,我们会选择这些样本值中最高的一个,对应的臂被认为是最有可能给出最高回报的臂,因此在当前步骤中被选择。这种方法允许我们在探索(试验不同的臂)和利用(选择最好的臂)之间自然地找到平衡。

策略比较

  • ε-贪婪:简单易实现,但可能在探索和利用之间不够平衡。
  • UCB:平衡探索和利用,适用于固定和已知的臂数量。
  • 汤普森采样:提供了很好的平衡,适应性强,但需要对臂的回报分布有先验假设。

每种策略都有其优势和适用场景,选择哪一种取决于具体问题的特性和可用信息。在实践中,经常需要根据实际情况调整策略参数,以达到最佳的性能。